# 获取教程、习题、案例，共同学习、讨论、打卡
# 请关注：Crossin的编程教室
# 学习交流或对代码有疑问请加交流群：
#【微信】sunset24678（添加时备注：python）
#【QQ】725903636（最新群号见 https://python666.cn/c/9）

# 本代码使用 pygame-zero 框架，需先安装：pip install pgzero
# 用此框架代码看起来与一般代码稍有不同，会有很多未定义的方法和变量，
# 在一些编辑器里会报错，但其实是可以运行的，无需手动增加 import。
# pgzero有两种方式运行，参见：https://pygame-zero.readthedocs.io/zh_CN/latest/ide-mode.html
# 本代码用的是第二种直接运行的方式（需新版pgzero）。
# 有部分读者反馈此代码在spyder上无法运行，类似情况可以尝试第一种传统方法：
# 把最后一行pgzrun.go()去掉，然后直接在命令行该目录下运行： pgzrun sheep.py

import pgzrun
import pygame
import random
import math

FAN_SPEED = 3       # 玩家最大速度
FAN_ACC = 0.3       # 玩家加速度
GUARD_SPEED = 2.8   # NPC最大速度
GUARD_ACC = 0.1     # NPC加速度

WIDTH = 1024
HEIGHT = 768

gametime = 0
tick = 0
hugcount = 0
gameend = False
guards = []

class Fan(Actor):
    def reset(self):
        self.pos = 100, HEIGHT-100
        self.fx = 0
        self.fy = 0
        self.speedx = 0
        self.speedy = 0
        self.speed_max = FAN_SPEED
        self.acc = FAN_ACC
        self.sprites = [f'11.png', f'12.png']
        self.index = 0
        self.interval = 0
        self.fps = 6
        self.image = self.sprites[self.index]
        self.angle_dest = 0

    def get_direct(self):
        # 跑动方向
        if keyboard.left:
            self.fx = -1
        elif keyboard.right:
            self.fx = 1
        else:
            self.fx = 0
        if keyboard.up:
            self.fy = -1
        elif keyboard.down:
            self.fy = 1
        else:
            self.fy = 0
        # 目标方向
        # directs = ((135, 180, -135), (90, None, -90), (45, 0, -45))
        # direct = directs[self.fx + 1][self.fy + 1]
        # if direct is None:
        #     self.angle_dest = self.angle
        # else:
        #     self.angle_dest = direct        
        self.angle_dest = self.angle + 400
        if self.fx * self.fy != 0:
            self.fx *= 0.707
            self.fy *= 0.707

    def update(self, dt):
        # 跑步动画
        self.interval += dt
        if self.interval > 1 / self.fps:
            self.index = (self.index + 1) % len(self.sprites)
            self.interval = 0
        self.image = self.sprites[self.index]

        self.get_direct()
        # 贴图转向
        if abs(self.angle - self.angle_dest) < 5 or abs(self.angle - self.angle_dest) > 355:
            self.angle = self.angle_dest
        else:
            if (0 < self.angle - self.angle_dest < 180) or (
                self.angle - self.angle_dest < -180):
                self.angle -= 500 * dt
            else:
                self.angle += 500 * dt
        self.angle = (self.angle + 180) % 360 - 180
        # 加速
        self.speedx += self.fx * self.acc
        self.speedy += self.fy * self.acc
        d = (self.speedx**2 + self.speedy**2)**0.5 / self.speed_max
        if d > 1:
            self.speedx /= d
            self.speedy /= d
        self.x += self.speedx
        self.y += self.speedy

fan = Fan('11')
fan.reset()

class Guard(Fan):
    def reset(self):
        super().reset()
        self.sprites = [f'21.png', f'22.png']
        self.speed_max = GUARD_SPEED + random.random() * 0.5
        self.acc = GUARD_ACC + random.random() * 0.1
        self.pos = random.randint(0, WIDTH), -50

    def get_direct(self):
        # 跑动方向
        d_y = fan.y - self.y
        d_x = fan.x - self.x
        if d_x == 0:
            if d_y > 0:
                self.angle_dest = -90
            else:
                self.angle_dest = 90
        else:
            self.angle_dest = math.atan(-d_y / d_x) * 180 / math.pi
            if d_x < 0:
                self.angle_dest += 180

        self.fx = math.cos(self.angle_dest/180*math.pi)
        self.fy = -math.sin(self.angle_dest/180*math.pi)

        # print(self.fx, self.fy)

    def catch(self):
        global gameend
        # 跑动方向
        d_y = fan.y - self.y
        d_x = fan.x - self.x
        dis = (d_x**2+d_y**2)**0.5
        if dis < 64:
            # gameend = True
            animate(self, pos=(self.x-1000*math.cos(self.angle_dest/180*math.pi), self.y+1000*math.sin(self.angle_dest/180*math.pi)), duration=0.5)


# guard = Guard('21')
# guard.reset(2)
# guard.pos = 0, 0


class Messi(Guard):
    def reset(self):
        self.pos = WIDTH-200, HEIGHT-200
        self.progress = 0
        self.hug = False
        self.texttime = 0

    def update(self, dt):
        global hugcount
        # 朝向
        self.get_direct()
        self.angle = self.angle_dest
        dis = ((fan.x - self.x) ** 2 + (fan.y - self.y) ** 2)**0.5
        if dis < 64:
            if not self.hug:
                self.progress += dt * 100
                if self.progress >= 64:
                    self.progress = 0
                    self.hug = True
                    self.texttime = 1
                    hugcount += 1
        else:
            self.progress = 0
            self.hug = False
        if self.texttime > 0:
            self.texttime -= dt

    def draw_bar(self):
        if self.progress > 0:
            screen.draw.filled_rect(Rect((self.x-32, self.y-32), (self.progress, 4)), 'white')
        if self.texttime > 0:
            screen.draw.text('+1', (self.x, self.y+self.texttime*32-64), fontsize=50, anchor=(0.5, 0.5))

messi = Messi('3')
messi.reset()

def draw():
    if gameend:
        screen.blit('end', (0,0))
        screen.draw.text(f'TIME: {gametime:.1f}', (WIDTH/2, 100), fontsize=100, anchor=(0.5, 0), owidth=1)
        screen.draw.text(f'HUG: {hugcount:d}', (WIDTH/2, 200), fontsize=100, anchor=(0.5, 0), owidth=1)
        return
    screen.clear()
    screen.blit('ground', (0,0))
    fan.draw()
    for guard in guards:
        guard.draw()
    messi.draw()
    messi.draw_bar()
    screen.draw.text(f'TIME: {gametime:.1f}', (20, 20), fontsize=50, anchor=(0, 0))
    screen.draw.text(f'HUG: {hugcount:d}', (WIDTH-20, 20), fontsize=50, anchor=(1, 0))

def update(dt):
    global gametime, tick
    if not gameend:
        fan.update(dt)
        for guard in guards:
            guard.update(dt)
            guard.catch()
        messi.update(dt)
        gametime += dt
        tick -= dt
        if tick < 0:
            guard = Guard('21')
            guard.reset()
            guards.append(guard)
            tick = 1

pgzrun.go()